/*
 * Recipe.java
 *
 * Created on 15 de abril de 2007, 17:44
 */

package servidor;


import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
//import com.elharo.xml.xinclude.DOMXIncluder;


import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.Element;




import java.util.*;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.NodeList;

/**
 *
 * @author Pellizard
 */
public class Recipe {
   
    private int id;
    private String title;
    private Vector authors;
    private String license;
    private int revision;
    //private int id;
    private Vector tags;
    private String recipesAbstract;
    private Vector revisions;
    private Vector ingredients;
    
    /**
     * Creates a new instance of Recipe
     */
    public Recipe( String titulo, Vector autores, int id, Vector tags, String introduccion, Vector revisiones, Vector ingredients, int revision ) {
        
        this.setTitle(titulo);
        //this.setLicencia(licencia);
        this.setId(id);
        this.setTags(tags);
        this.setRecipesAbstract(introduccion);
        this.setRevisions(revisiones);
        this.setAuthors(autores);
        this.ingredients = ingredients;
        this.revision = revision;
    }
    
    public void save( String name ){
        try
        {
            OutputStream fout= new FileOutputStream(Util.getHowtosDirectory()+name );
            TransformerFactory transFactory = TransformerFactory.newInstance();
            Transformer transformer = transFactory.newTransformer();
            DOMSource source = new DOMSource(this.toXml());
            StreamResult result = new StreamResult(fout);
            transformer.transform(source, result);
        }
        catch( Exception e ){  };
    }
    
    public String getFileName(){
        
        String dir = Util.getHowtosDirectory()  ;
        String file = this.getTitle() + "_" + ((Author)this.getAuthors().get(0)).getCompleteName() + "_" + this.revision + ".xml" ;
        try{
            String path = dir + URLEncoder.encode(file, "UTF8");
            return path;
        }
        catch(UnsupportedEncodingException e){
            return "error";
        }
        
    }

    public Index generateIndex()
    {
        Index index = new Index();
        
        for( int i = 0 ; i < ingredients.size() ; i++ )
        {
            Ingredient ingredient = ((Ingredient)ingredients.get( i ));
            
            Index subIndex = new Index();
            for( int j = 0; j < ingredient.getSteps().size(); j++ )
            {
                Vector x = ingredient.getSteps();
                Step step = (Step)ingredient.getSteps().get( j );
                subIndex.addChapter( new Chapter( j, step.getTitle(), new Index() ) );
            }
            
            index.addChapter( new Chapter( i, ingredient.getTitle(), subIndex ));
        }
        
        return index;
    }
    
    public String getTitle() {
        return title;
    }

    public void setTitle(String titulo) {
        this.title = titulo;
    }

    
    public Vector getIngredients(){
        return ingredients;
    }


    public Vector getAuthors() {
        return authors;
    }

    public void setAuthors(Vector autores) {
        this.authors = autores;
    }

    public String getLicencia() {
        return license;
    }

    public void setLicencia(String licencia) {
        this.license = licencia;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public Vector getTags() {
        return tags;
    }

    public void setTags(Vector tags) {
        this.tags = tags;
    }

    public String getAbstract() {
        return getRecipesAbstract();
    }

    public void setAbstract(String introduccion) {
        this.setRecipesAbstract(introduccion);
    }

    public Vector getRevisions() {
        return revisions;
    }
    
    public void setRevisions(Vector revisiones) {
        this.revisions = revisiones;
    }
    
    public void addRevision( Revision revision )
    {
        revisions.add( revision );
    }
    
    public Node toXml(){
      try
      {
          DocumentBuilderFactory fact = DocumentBuilderFactory.newInstance();
          DocumentBuilder parser = fact.newDocumentBuilder();
          Document doc = parser.newDocument();
          
          return toXml( doc );
      }
      catch( Exception ex )
      {
         return null;
      }
  }
    
    public Node toXml( Document doc ){
         
      try {
      
      
      Element root = doc.createElement("recipe");
      
      root.setAttribute("xmlns:xi", "http://www.w3.org/2001/XInclude");
      
      Node intro = doc.createElement("intro"); 
      
      Node id = doc.createElement("id");
      id.appendChild( doc.createTextNode( String.valueOf(this.getId()) ) );
      intro.appendChild(id);
      
      Node revision = doc.createElement("reciperevision");
      revision.appendChild( doc.createTextNode( String.valueOf(this.getRevision()) ) );
      intro.appendChild(revision);
      
      Node title = doc.createElement("title");
      title.appendChild( doc.createTextNode(this.getTitle()) );
      intro.appendChild(title);
      
      Node autores = doc.createElement("authors");
      
      for( int i = 0; i < this.getAuthors().size(); i++ ){
          autores.appendChild( ((Author) this.getAuthors().get(i)).toXml( doc ) );
      }
      
      intro.appendChild(autores);
      
      Node revisiones = doc.createElement( "revisions" );
      
      for( int i = 0; i < this.getRevisions().size(); i++ ){
          revisiones.appendChild( ((Revision) this.getRevisions().get(i)).toXml( doc ) );
      }
      
      intro.appendChild(revisiones);
      
      Node tags = doc.createElement("tags");
      
      for( int i = 0; i < this.getTags().size(); i++ ){
          Node tag = doc.createElement( "tag" );
          tag.appendChild( doc.createTextNode( (String)this.getTags().get(i) ) );
          tags.appendChild(tag);
      }
      
      intro.appendChild(tags);
      
      Node contenido = doc.createElement( "contenido" );
      
      Node recipeAbstract = doc.createElement( "abstract" );
      recipeAbstract.appendChild( doc.createTextNode( this.getAbstract() ) );
      contenido.appendChild(recipeAbstract);
      
      contenido.appendChild( this.generateIndex().toXml( doc ) );
      
      Node ingredients = doc.createElement("ingredients");
      for (int l = 0; l < this.ingredients.size(); l++){
          Ingredient ing = (Ingredient) this.ingredients.get(l);
          Element ingredient = doc.createElementNS("http://www.w3.org/2001/XInclude", "xi:include"); //agregar aca el xinclude!!!!
          //Element ingredient = doc.createElement("include"); 
          //ingredient.setPrefix("xi"); //ambiguo / redundante con lo de arriba, pero sino no pone el prefix
          
          ingredient.setAttribute("href", ing.getFileName());
          
          ingredients.appendChild(ingredient);
      }
      contenido.appendChild(ingredients);
      
      root.appendChild(intro);
      root.appendChild(contenido);
      
      return root;

    } catch (Exception ex) {
      System.err.println("+============================+");
      System.err.println("|        XML Error           |");
      System.err.println("+============================+");
      System.err.println(ex.getClass());
      System.err.println(ex.getMessage());
      System.err.println("+============================+");
      ex.printStackTrace();
      return null;
    }
      
    }
    
    public Document xtoXml()throws Exception{
        
      try {
      // create a SchemaFactory that conforms to W3C XML Schema
      //SchemaFactory sf = SchemaFactory.newInstance(XMLConstants.RELAXNG_NS_URI);
      // set your error handler to catch errors during schema construction
      //sf.setErrorHandler(myErrorHandler); //construir el errorhandler!!!
      // parse the purchase order schema
      //Schema schema = sf.newSchema("recipe.rng");
      
      DocumentBuilderFactory fact = DocumentBuilderFactory.newInstance();
      fact.setNamespaceAware(true); //se validara
      //fact.setSchema(schema); //...segun el schema anterior
      fact.setXIncludeAware(true); //se procesan los xincludes de ingredients
      
      DocumentBuilder parser = fact.newDocumentBuilder();
      //parser.setErrorHandler(myErrorHandler); //CREAR ERRORHANDLER!!!!
      
      Document doc = parser.newDocument();

      Element recipe= doc.createElement("recipe");
      
      doc.appendChild(recipe);
      //ver como se agrega el namespace que se necesite...(ej: xinclude)
      
      //principio de introduccion
      Node introduccion = doc.createElement("intro");
      //recipesAbstract.appendChild(doc.createTextNode(this.recipesAbstract));
      
      
      Node id = doc.createElement("id");
      id.appendChild(doc.createTextNode(""+this.id));
      introduccion.appendChild(id);
      
      Node revision = doc.createElement("reciperevision");
      revision.appendChild( doc.createTextNode( String.valueOf(this.getRevision()) ) );
      introduccion.appendChild(revision);
      
      Node titulo = doc.createElement("title");
      titulo.appendChild(doc.createTextNode(this.title));
      introduccion.appendChild( titulo );
      
      
      Node autores = doc.createElement("authors");
      
      for ( int i=0; i < this.getAuthors().size(); i++ ){
          Author a = (Author) this.getAuthors().get(i);
          autores.appendChild(a.toXml(doc));
      }
      
      introduccion.appendChild(autores);

      Node revisiones = doc.createElement("revisions");
      for (int j = 0; j < this.revisions.size(); j++){
          Revision r = (Revision) this.revisions.get(j);
          revisiones.appendChild(r.toXml(doc));
      }
      introduccion.appendChild(revisiones);
      
      Node tags = doc.createElement("tags");
      for (int k = 0; k < this.tags.size(); k++){
          Node tag = doc.createElement("tag");
          tag.appendChild(doc.createTextNode((String)this.tags.get(k)));
          tags.appendChild(tag);
      }
      introduccion.appendChild(tags);
      
      recipe.appendChild(introduccion);
      //fin de <introduccion >
      
      // principio de <contenido>
      Node contenido = doc.createElement("contenido");
      
      
      Node recipesAbstract =  doc.createElement("abstract");
      recipesAbstract.appendChild(doc.createTextNode(this.getRecipesAbstract()));
      contenido.appendChild(recipesAbstract);
      
      //Node index = doc.createElement("index");
      //index.appendChild(this.generateIndex().toXml());
      contenido.appendChild(this.generateIndex().toXml( doc ));
      
      Node ingredients = doc.createElement("ingredients");
      for (int l = 0; l < this.ingredients.size(); l++){
          Ingredient ing = (Ingredient) this.ingredients.get(l);
          //Element ingredient = doc.createElementNS("http://www.w3.org/2003/XInclude", "include"); //agregar aca el xinclude!!!!
          //ingredient.setPrefix("xi"); //ambiguo / redundante con lo de arriba, pero sino no pone el prefix
          
          //ingredient.setAttribute("href", "ingredients/"+ ing.getFileName());
          
          //ingredients.appendChild(ingredient);
      }
      contenido.appendChild(ingredients);
      
      
      recipe.appendChild(contenido);
      
      // fin de <contenido>
      
      //fin de recipe
      
      return doc;

    } catch (Exception ex) {
       
      System.err.println("+============================+");
      System.err.println("|        XML Error           |");
      System.err.println("+============================+");
      System.err.println(ex.getClass());
      System.err.println(ex.getMessage());
      System.err.println("+============================+");
      
      ex.printStackTrace();
      
      return null;
    
    }
    }
        
        public static Recipe fromXml( String str )
  {
        
        int id = 0;
        String title = "";
        Vector authors = new Vector();
        String license = "";
        int revision = 0;
        Vector tags = new Vector();
        String recipesAbstract = "";
        Vector revisions = new Vector();
        Vector ingredients = new Vector();
        
        ByteArrayInputStream is;
        try{
             is = new ByteArrayInputStream( str.getBytes("UTF8") );
        }catch(Exception e){
            return null;
        }
        
        try
        {
            DocumentBuilderFactory fact = DocumentBuilderFactory.newInstance();
            fact.setXIncludeAware(true); 
            fact.setNamespaceAware(true);
            DocumentBuilder parser = fact.newDocumentBuilder();
            
            Document doc = parser.parse( is );
            //doc = DOMXIncluder.merge(doc, null);
            
            //test
            NodeList nosdes = doc.getElementsByTagName( "recipe" );
            Node no = nosdes.item(0);
            //end test
            
            
            NodeList nodes = doc.getElementsByTagName( "id" );
            if( nodes.getLength() == 1 )
            {
                Node node = nodes.item( 0 );
                id = Integer.parseInt( node.getFirstChild().getNodeValue() );
            }
            
            nodes = doc.getElementsByTagName( "reciperevision" );
            if( nodes.getLength() == 1 )
            {
                Node node = nodes.item( 0 );
                revision = Integer.parseInt( node.getFirstChild().getNodeValue() );
            }
            
            nodes = doc.getElementsByTagName( "title" );
            if( true ) // nodes.getLength() == 1 ) hay varios tags title...solo un fix rapido
            {
                Node node = nodes.item( 0 );
                title = node.getFirstChild().getNodeValue();
            }
            
            nodes = doc.getElementsByTagName( "abstract" );
            if( nodes.getLength() == 1 )
            {
                Node node = nodes.item( 0 );
                recipesAbstract = node.getFirstChild().getNodeValue();
            }
            
            nodes = doc.getElementsByTagName( "tag" );
            
            for( int i = 0 ; i < nodes.getLength() ; i++ )
            {
                Node node = nodes.item( i );
                tags.add( node.getFirstChild().getNodeValue() );
            }
            
            nodes = doc.getElementsByTagName("authors");
            Node nod = nodes.item(0);
            NodeList nodeList = nod.getChildNodes();
            for(int i=0;i<nodeList.getLength();i++){
                
                Node nodeAuthor = nodeList.item(i);
                
                Author author = Author.fromXml( Util.node2String( nodeAuthor ) );
                
                authors.addElement(author);
            }
            
            nodes = doc.getElementsByTagName( "revision" );
            
            for( int i = 0 ; i < nodes.getLength() ; i++ )
            {
                Node node = nodes.item( i );
                revisions.add( Revision.fromXml( Util.node2String(node) ) );
            }
            
            nodes = doc.getElementsByTagName( "ingredient" );
            for( int i = 0 ; i < nodes.getLength() ; i++ )
            {
                Node node = nodes.item( i );
                ingredients.add( Ingredient.fromXml( Util.node2String(node) ) );
            }
            
            return new Recipe( title, authors,id,tags,recipesAbstract,revisions,ingredients, revision );
        }
        catch( Exception ex )
        {
            ex.printStackTrace();
            return null;
        }
  }

    
    public String getRecipesAbstract() {
        return recipesAbstract;
    }

    public void setRecipesAbstract(String recipesAbstract) {
        this.recipesAbstract = recipesAbstract;
    }
    
    public String toString()
    {
        TransformerFactory transFactory = TransformerFactory.newInstance();
        try
        {
            Transformer transformer = transFactory.newTransformer();
            DOMSource source = new DOMSource(this.toXml());
            ByteArrayOutputStream s = new ByteArrayOutputStream();
            StreamResult result = new StreamResult(s);
            transformer.transform(source, result);
            return s.toString();
        }
        catch( Exception e ){  };
        return "";
    }

    public int getRevision() {
        return revision;
    }

    public void setRevision(int revision) {
        this.revision = revision;
    }

}
